﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Media.Animation;


namespace RedditViewer
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        Subreddit sr;
        int PostIndex = 0;
        TaskScheduler uiScheduler;
        int CurrentCanvas = 2;
        Post loadingPost = null;
        Post CurrentPost = null;
        bool MouseInWindow = false;
        DateTime TimeOfLastPostChange = DateTime.Now;
        System.Windows.Threading.DispatcherTimer dispatcherTimer;
        bool Paused = false;
        private void Window_Loaded_1(object sender, RoutedEventArgs e)
        {
            Application.Current.DispatcherUnhandledException += Current_DispatcherUnhandledException;

            

            progressBar.Minimum = 0;
            progressBar.Maximum = 100;
            canvas1.Opacity = 0.0;
            canvas2.Opacity = 0.0;
            arrowPrev.Visibility = System.Windows.Visibility.Hidden;
            arrowNext.Visibility = System.Windows.Visibility.Hidden;
            pnlDetails.Visibility = System.Windows.Visibility.Hidden;


            LoadSettings();


            dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
            dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
            dispatcherTimer.Interval = TimeSpan.FromSeconds(1);

            ApplySettings();

        }

        void Current_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
        {
#if DEBUG   // In debug mode do not custom-handle the exception, let Visual Studio handle it
            e.Handled = false;
#else
                ShowUnhandeledException(e);    
#endif
        }

        void ShowUnhandeledException(DispatcherUnhandledExceptionEventArgs e)
        {
            e.Handled = true;
            string errorMessage = string.Format("An application error occurred.\nPlease check whether your data is correct and repeat the action. If this error occurs again there seems to be a more serious malfunction in the application, and you better close it.\n\nError:{0}\n\nDo you want to continue?\n(if you click Yes you will continue with your work, if you click No the application will close)",
            e.Exception.Message + (e.Exception.InnerException != null ? "\n" +
            e.Exception.InnerException.Message : null));
            if (MessageBox.Show(errorMessage, "Application Error", MessageBoxButton.YesNoCancel, MessageBoxImage.Error) == MessageBoxResult.No)
            {
                if (MessageBox.Show("WARNING: The application will close. Any changes will not be saved!\nDo you really want to close it?", "Close the application!", MessageBoxButton.YesNoCancel, MessageBoxImage.Warning) == MessageBoxResult.Yes)
                {
                    Application.Current.Shutdown();
                }
            }
        }

        private void dispatcherTimer_Tick(object sender, EventArgs e)
        {
            if (loadingPost == null 
                && Properties.Settings.Default.Slideshow
                && !Paused
                && (DateTime.Now - TimeOfLastPostChange).TotalSeconds > Properties.Settings.Default.SlideshowSpeed)
                ChangePost(1, false);
        }


        string currentStatus;
        void SetStatus(string s)
        {
            SetStatus(s, 1);
        }
        void SetStatus(string s, double? percentLoaded)
        {
            this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
            {
                if (s != null)
                {
                    if (s == null)
                        lblStatus.Visibility = System.Windows.Visibility.Hidden;
                    else
                        lblStatus.Visibility = System.Windows.Visibility.Visible;
                    lblStatus.Content = s;
                    currentStatus = s;
                }

                if (percentLoaded.HasValue)
                {
                    if (percentLoaded >= 1 || percentLoaded == 0)
                        progressBar.Visibility = System.Windows.Visibility.Hidden;
                    else
                    {
                        progressBar.Visibility = System.Windows.Visibility.Visible;
                        progressBar.Value = 100 * percentLoaded.Value;
                    }
                }

            }));
        }

        void ChangePercentLoaded(double percentLoaded)
        {
            SetStatus(null, percentLoaded);
        }


        void ChangePost(int offset, bool Fast)
        {
            PostIndex += offset;
            if (PostIndex < 0) PostIndex = 0;
            if (sr == null)
                return;
            Post p = sr.GetPost(PostIndex);
            if (p == null) return;

            if (loadingPost != null && loadingPost != p)
            {
                loadingPost.CancelLoad();
                System.Diagnostics.Debug.WriteLine("Cancelled " + loadingPost.Uri);
                loadingPost = null;
            }

            SetStatus("Loading " + p.Uri, 0);
            Task.Factory.StartNew(() =>
            {
                loadingPost = p;
                p.Load(x => ChangePercentLoaded(x), Properties.Settings.Default.SaveImages ?  Properties.Settings.Default.SaveFolder : null);
            }).ContinueWith(x =>
            {
                if (loadingPost != null && loadingPost != p)
                    return;
                if (MouseInWindow && CurrentPost == null)
                    this.Window_MouseEnter_1(null, null);

                loadingPost = null;
                CurrentPost = p;
                lblStatus.Content = "loaded";
                BitmapImage b = p.Image;
                if (b != null)
                {
                    SetStatus("", 1.0);
                    lblPostTitle.Content = this.Title = p.Title + " [" + (int)b.Width + "x" + (int)b.Height + "]";
                    lblUps.Content = p.Ups.ToString("#,##0");
                    lblDowns.Content = p.Downs.ToString("#,##0");
                    lblComments.Content = p.NumComments.ToString("#,##0");
                    lblPoints.Content = p.Score.ToString("#,##0");
                    lblTime.Content = p.Age.ToAgoString();
                    lblUser.Content = p.Author;

                    
                    ImageBrush img = new ImageBrush(b);
                    img.Stretch = Properties.Settings.Default.Stretch;
                    img.TileMode = TileMode.None;
                    

                    if (CurrentCanvas == 2)
                    {
                        CurrentCanvas = 1;
                        canvas1.Background = img;
                        Fade(canvas1, false, Fast);
                        Fade(canvas2, true, Fast);
                    }
                    else
                    {
                        CurrentCanvas = 2;
                        canvas2.Background = img;
                        Fade(canvas1, true, Fast);
                        Fade(canvas2, false, Fast);
                    }
                    TimeOfLastPostChange = DateTime.Now;
                    if (Properties.Settings.Default.SetWallpaper)
                        Utils.SetWallpaper(b, Utils.WallpaperStyle.Stretched);
                }
                else
                {
                    loadingPost = null;
                    lblStatus.Content = "Failed to load " + p.Url;
                    TimeOfLastPostChange = DateTime.Now.AddDays(-1);
                }
                
            }, uiScheduler);
        }

        void Fade(Canvas c, bool fadeOut, bool Fast)
        {
            Storyboard storyboard = new Storyboard();

            TimeSpan duration = new TimeSpan(0, 0, 0, 0, Fast ? 500 : Properties.Settings.Default.TransitionSpeed);
            DoubleAnimation animation = new DoubleAnimation();
            if (fadeOut)
            {
                animation.From = 1.0;
                animation.To = 0.0;
            }
            else
            {
                animation.From = 0.0;
                animation.To = 1.0;
            }
            animation.Duration = new Duration(duration);
            Storyboard.SetTargetName(animation, c.Name);
            Storyboard.SetTargetProperty(animation, new PropertyPath(Control.OpacityProperty));
            storyboard.Children.Add(animation);
            storyboard.Begin(this);
        }

        private void Window_KeyUp_1(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Right)
                ChangePost(1, true);
            else if (e.Key == Key.Left)
                ChangePost(-1, true);
            else if (e.Key == Key.Escape && this.IsKeyboardFocusWithin)
                Application.Current.Shutdown();
        }

        private void Window_MouseUp_1(object sender, MouseButtonEventArgs e)
        {
            if (e.ChangedButton == MouseButton.XButton1 && e.ButtonState == MouseButtonState.Released)
            {
                ChangePost(-1, true);
            }
            else if (e.ChangedButton == MouseButton.XButton2 && e.LeftButton == MouseButtonState.Released)
            {
                ChangePost(1, true);
            }
        }

        private void Window_MouseWheel_1(object sender, MouseWheelEventArgs e)
        {
            ChangePost(e.Delta > 0 ? -1 : 1, true);
        }

        private void Window_MouseDoubleClick_1(object sender, MouseButtonEventArgs e)
        {
            if (this.WindowState == System.Windows.WindowState.Maximized)
                this.WindowState = System.Windows.WindowState.Normal;
            else
                this.WindowState = System.Windows.WindowState.Maximized;
        }

        private void Window_MouseEnter_1(object sender, MouseEventArgs e)
        {
            if (CurrentPost == null) return;
            MouseInWindow = true;
            pnlDetails.Opacity = arrowPrev.Opacity = arrowNext.Opacity = 0.0; 
            pnlDetails.Visibility = arrowPrev.Visibility = arrowNext.Visibility = System.Windows.Visibility.Visible;
            

            DoubleAnimation da = new DoubleAnimation();
            da.From = 0.0;
            da.To = 1.0;
            da.Duration = new Duration(TimeSpan.FromSeconds(0.5));
            pnlDetails.BeginAnimation(Canvas.OpacityProperty, da);
            arrowPrev.BeginAnimation(Canvas.OpacityProperty, da);
            arrowNext.BeginAnimation(Canvas.OpacityProperty, da);
            
        }

        private void Window_MouseLeave_1(object sender, MouseEventArgs e)
        {
            MouseInWindow = false;


            DoubleAnimation da = new DoubleAnimation();
            da.From = 1.0;
            da.To = 0.0;
            da.Duration = new Duration(TimeSpan.FromSeconds(0.5));
            pnlDetails.BeginAnimation(Canvas.OpacityProperty, da);
            arrowPrev.BeginAnimation(Canvas.OpacityProperty, da);
            arrowNext.BeginAnimation(Canvas.OpacityProperty, da);
            
        }

        private void pnlDetails_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ClickCount > 1)
            {
                Post p = CurrentPost;
                if (p == null) return;
                var startInfo = new System.Diagnostics.ProcessStartInfo
                {
                    FileName = "http://www.reddit.com" + p.PermaLink
                };

                System.Diagnostics.Process.Start(startInfo);
                e.Handled = true;
            }
        }


        private void arrowNext_PreviewMouseUp(object sender, MouseButtonEventArgs e)
        {
            ChangePost(1, true);
            e.Handled = true;
        }
        private void arrowNext_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;
        }

        private void arrowPrev_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            ChangePost(-1, true);
            e.Handled = true;
        }

        private void arrowPrev_PreviewMouseUp(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;
        }



        void LoadSettings()
        {
            Width = Properties.Settings.Default.WindowWidth;
            Height= Properties.Settings.Default.WindowHeight;
            Top = Properties.Settings.Default.WindowTop;
            Left= Properties.Settings.Default.WindowLeft;
            WindowState = Properties.Settings.Default.WindowState;
        }

        void SaveSettings()
        {
            Properties.Settings.Default.WindowWidth = Width;
            Properties.Settings.Default.WindowHeight= Height;
            Properties.Settings.Default.WindowTop = Top;
            Properties.Settings.Default.WindowLeft = Left;
            Properties.Settings.Default.WindowState= WindowState;
            Properties.Settings.Default.Save();
        }

        private void Window_Closing_1(object sender, System.ComponentModel.CancelEventArgs e)
        {
            SaveSettings();
        }



        private void mnuSettings_Click(object sender, RoutedEventArgs e)
        {
            SettingsDialog dlg = new SettingsDialog();
            if (dlg.ShowDialog() == true)
                ApplySettings();
        }

        void ApplySettings()
        {
            uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
            if (dispatcherTimer != null)
                dispatcherTimer.Stop();

            Task.Factory.StartNew(() =>
            {
                string currentSR = (sr != null ? sr.Name : null);

                if (Properties.Settings.Default.UseSubscribedSubreddits)
                {
                    SetStatus("Logging in");
                    ApiHelper.Login();

                    SetStatus("Loading subreddits");
                    var msr = ApiHelper.GetMySubreddits();
                    string subreditList = String.Join("+", msr.Select(_ => _.display_name));
                    if (sr == null || sr.Name != subreditList)
                        sr = new Subreddit(subreditList);
                }
                else
                {
                    string subredditList = String.Join("+", Properties.Settings.Default.Subreddits.Cast<string>());
                    if (sr == null || sr.Name != subredditList)
                        sr = new Subreddit(subredditList);
                }

                if (currentSR != sr.Name)
                {
                    SetStatus("Loading " + sr.Name);
                    try
                    {
                        sr.Load();
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                        sr = null;
                    }
                }
            }).ContinueWith(x =>
            {
                ChangePost(0, false);
                dispatcherTimer.Start();
            }, uiScheduler); ;          
        }

        private void Window_MouseLeftButtonDown_1(object sender, MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonDown(e);

            // Begin dragging the window
            this.DragMove();
        }

        

        private void btnClose_Click(object sender, RoutedEventArgs e)
        {
            Application.Current.Shutdown();
        }

        private void Window_MouseMove_1(object sender, MouseEventArgs e)
        {
            if (!MouseInWindow)
                Window_MouseEnter_1(sender, e);
        }

        private void mnuSetWallpaper_Click(object sender, RoutedEventArgs e)
        {
            if (CurrentPost != null && CurrentPost.Image != null)
            {
                    Utils.SetWallpaper(CurrentPost.Image, Utils.WallpaperStyle.Stretched);
            }
        }

        private void mnuPlayPause_Click(object sender, RoutedEventArgs e)
        {
            
            Paused = !Paused;
            MenuItem mnu  = (MenuItem)sender;
            ContextMenu cm = mnu.Parent as ContextMenu;
            cm.IsOpen = false;
            
            if (mnu.Header.ToString().Contains("Pause"))
            {
                Paused = true;
                mnu.Visibility = System.Windows.Visibility.Collapsed;
                cm.Items.Cast<MenuItem>().Where(_ => _.Header.ToString().Contains("Play")).Single().Visibility = System.Windows.Visibility.Visible;
            }
            else
            {
                Paused = false;
                mnu.Visibility = System.Windows.Visibility.Collapsed;
                cm.Items.Cast<MenuItem>().Where(_ => _.Header.ToString().Contains("Pause")).Single().Visibility = System.Windows.Visibility.Visible;
            }
        }

        private void MenuItem_Click_1(object sender, RoutedEventArgs e)
        {
            if (Properties.Settings.Default.SaveFolder != null)
            {
                try
                {
                    if (CurrentPost == null || CurrentPost.Subreddit == null)
                        System.Diagnostics.Process.Start(Properties.Settings.Default.SaveFolder);
                    else
                        System.Diagnostics.Process.Start(System.IO.Path.Combine(Properties.Settings.Default.SaveFolder, CurrentPost.Subreddit));
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "Unable to open folder", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }

        }
    }
}
